package docker

import (
	"fmt"
	"os"
	"os/exec"
	"strings"
	"time"

	"pkg-tools/errors"
)

const (
	DockerStatusExited   = "exited"   // 容器已退出
	DockerStatusRunning  = "running"  // 容器正在运行
	DockerStatusStarting = "starting" // 容器正在启动
)

type Docker struct {
	ContainerID   string // 容器ID
	ContainerName string // 容器名称
}

type PortMapping struct {
	HostPort      string // 主机端口
	ContainerPort string // 容器内端口
}

type ContainerOptions struct {
	Name         string            // 容器名称
	ImageName    string            // 镜像名称
	Options      map[string]string // 环境变量选项
	MountPath    string            // 挂载路径
	PortMappings []PortMapping     // 端口映射列表
}

// IsInstalled 检查Docker是否安装并运行。
func (d *Docker) IsInstalled() bool {
	cmd := exec.Command("docker", "version")
	err := cmd.Run()
	return err == nil
}

// Start 使用给定的选项启动一个新的容器。
func (d *Docker) Start(c ContainerOptions) (string, error) {
	if err := d.RemoveIfExists(); err != nil && !strings.Contains(err.Error(), "No such container") {
		return "", fmt.Errorf("删除现有容器失败: %v", err)
	}

	dockerArgs := d.getDockerRunOptions(c)
	cmd := exec.Command("docker", dockerArgs...)
	cmd.Stderr = os.Stderr
	output, err := cmd.Output()
	if err != nil {
		return string(output), fmt.Errorf("启动容器失败: %v", err)
	}

	// 获取新创建容器的 ID
	containerIDCmd := exec.Command("docker", "ps", "-q", "-l", "--filter", "name="+c.Name)
	containerIDOutput, err := containerIDCmd.Output()
	if err != nil {
		return string(output), fmt.Errorf("获取容器ID失败: %v", err)
	}
	d.ContainerID = strings.TrimSpace(string(containerIDOutput))
	d.ContainerName = c.Name

	return string(output), nil
}

// Stop 如果容器存在则停止并移除它。
func (d *Docker) Stop() error {
	if d.ContainerID == "" && d.ContainerName == "" {
		return nil // 没有提供标识符，无需停止任何东西
	}

	var idOrName string
	if d.ContainerID != "" {
		idOrName = d.ContainerID
	} else {
		idOrName = d.ContainerName
	}

	cmd := exec.Command("docker", "rm", "-f", idOrName)
	return cmd.Run()
}

// getDockerRunOptions 构建 'docker run' 的命令行参数。
func (d *Docker) getDockerRunOptions(c ContainerOptions) []string {
	var args []string
	args = append(args, "run", "-d", "--name", c.Name)

	// 多个端口映射
	for _, pm := range c.PortMappings {
		args = append(args, "-p", fmt.Sprintf("%s:%s", pm.HostPort, pm.ContainerPort))
	}

	// 环境变量
	for key, val := range c.Options {
		args = append(args, "-e", fmt.Sprintf("%s=%s", key, val))
	}

	// 挂载临时文件系统
	if c.MountPath != "" {
		args = append(args, "--tmpfs", c.MountPath)
	}

	// 镜像名称
	args = append(args, c.ImageName)

	return args
}

// RemoveIfExists 如果容器存在则移除它。
func (d *Docker) RemoveIfExists() error {
	cmd := exec.Command("docker", "ps", "-a", "-q", "-f", "name="+d.ContainerName)
	output, err := cmd.CombinedOutput()
	if err != nil {
		return err
	}
	containerID := strings.TrimSpace(string(output))
	if containerID == "" {
		return nil // 容器不存在
	}
	d.ContainerID = containerID
	return d.Stop()
}

// WaitForStartOrKill 等待容器进入运行状态或在超时后终止它。
func (d *Docker) WaitForStartOrKill(timeout int) error {
	ticker := time.NewTicker(time.Second)
	defer ticker.Stop()

	for i := 0; i < timeout; i++ {
		status := d.getContainerStatus()
		switch status {
		case DockerStatusRunning:
			return nil
		case DockerStatusExited:
			return errors.New("容器在启动前已退出")
		default:
			select {
			case <-ticker.C:
			case <-time.After(time.Second):
				return errors.New("等待容器启动超时")
			}
		}
	}
	return d.Stop()
}

// getContainerStatus 获取容器的当前状态。
func (d *Docker) getContainerStatus() string {
	cmd := exec.Command("docker", "inspect", "-f", "{{.State.Status}}", d.ContainerName)
	output, err := cmd.CombinedOutput()
	if err != nil {
		return DockerStatusExited
	}
	return strings.TrimSpace(string(output))
}
